package cc.owoo.godpen.reflect;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.function.BiConsumer;
import java.util.regex.Pattern;

/**
 * 对象值反射，用于设置、获取对象的值
 * Created by nimensei
 * 2022-05-16 上午 09:39
 **/
public final class ObjectReflect<T> {
    private final Class<T> type;// 对象类型
    private final LinkedHashMap<String, ValueSet> capableSet = new LinkedHashMap<>();// 能够设置的值
    private final LinkedHashMap<String, ValueGet> capableGet = new LinkedHashMap<>();// 能够获取的值
    private final LinkedHashMap<String, ValueGet> existGet = new LinkedHashMap<>();// 每个可获取值的字段的真实名称
    private final LinkedList<RegexValueSet> regexSet = new LinkedList<>();// 使用正则表达式匹配能够设置的值

    private record RegexValueSet(String regex, ValueSet valueSet) {
    }

    /**
     * 构建值反射对象
     *
     * @param type  值类型
     * @param build 是否立即构建
     */
    ObjectReflect(Class<T> type, boolean build) {
        this.type = type;
        if (build)
            build();
    }

    /**
     * 构建值反射对象，将对象中的所有变量和函数进行判断，如果可以被设置或者被获取，那么就将其记录到值设置和获取的表中，后续调用这些表中的转换器即可完成值得设置和获取
     */
    void build() {
        if (type == null)
            throw new NullPointerException("type不能为空");
        for (Method method : type.getMethods()) {
            if (Modifier.isNative(method.getModifiers()))
                continue;
            if (method.isAnnotationPresent(Ignore.class))
                continue;
            KeyName keyName = method.getAnnotation(KeyName.class);
            if (keyName == null)
                buildCommonMethod(method);
            else
                buildKeyNameMethod(method, keyName.get().length() == 0 ? keyName.value() : keyName.get(), keyName.set());
        }
        for (Field field : type.getFields()) {
            if (Modifier.isStatic(field.getModifiers()) ||
                    Modifier.isPrivate(field.getModifiers()) ||
                    Modifier.isFinal(field.getModifiers()))
                continue;
            if (field.isAnnotationPresent(Ignore.class))
                continue;
            KeyName keyName = field.getAnnotation(KeyName.class);
            if (keyName == null)
                buildCommonField(field, null, null);
            else
                buildCommonField(field, keyName.get().length() == 0 ? keyName.value() : keyName.get(), keyName.set());
            bind(field.getAnnotation(KeyBind.class), field);
        }
    }

    /**
     * 添加绑定
     *
     * @param keyBind 键绑定
     * @param field   字段
     */
    private void bind(KeyBind keyBind, Field field) {
        if (keyBind == null)
            return;
        ClassType classType = ClassType.create(field.getGenericType());
        if (classType == null)
            return;
        Class<?> rowType = classType.rowType();
        if (Map.class.isAssignableFrom(rowType)) {
            TypeTranslate translate = Translate.createTranslate(classType.getGenericType(1));
            if (translate == null)
                return;
            for (String name : keyBind.value()) {
                MapSet mapSet = new MapSet(name, translate);
                capableSet.put(name, new KeyBindSet(field, mapSet));
            }
        } else {
            ObjectReflect<?> reflect = ObjectReflectBuild.create(rowType);
            for (String name : keyBind.value()) {
                ValueSet valueSet = reflect.getCapableSet(name);
                if (valueSet == null)
                    continue;
                capableSet.put(name, new KeyBindSet(field, valueSet));
            }
        }
    }

    /**
     * 添加值设置器
     *
     * @param names 映射名称
     * @param set   值设置器
     */
    private void appendValueSet(String[] names, ValueSet set) {
        if (names == null || names.length == 0)
            return;
        for (String s : names) {
            if (s == null || (s = s.trim()).length() == 0)
                continue;
            if (s.length() > 2 && s.charAt(0) == '/' && s.charAt(s.length() - 1) == '/')
                regexSet.add(new RegexValueSet(s.substring(1, s.length() - 1), set));
            else
                capableSet.put(s, set);
        }
    }

    /**
     * 构建普通函数值，判断名称前缀是否为set或get
     *
     * @param method 需要构建的函数
     */
    private void buildCommonMethod(Method method) {
        int parameterCount = method.getParameterCount();
        if (parameterCount >= 2)
            return;
        String rawName = method.getName();
        if (rawName.startsWith("set")) {
            if (parameterCount != 1)
                return;
            try {
                method.setAccessible(true);
            } catch (Exception e) {
                return;
            }
            String name = translateName(rawName.substring("set".length()));
            TypeTranslate valueTranslate = Translate.createTranslate(method.getGenericParameterTypes()[0]);
            capableSet.put(name, new MethodSet(method, valueTranslate));
        } else if (rawName.startsWith("get")) {
            if (parameterCount != 0 || method.getReturnType() == void.class)
                return;
            try {
                method.setAccessible(true);
            } catch (Exception e) {
                return;
            }
            String name = translateName(rawName.substring("get".length()));
            MethodGet methodGet = new MethodGet(method);
            capableGet.put(name, methodGet);
            existGet.put(rawName, methodGet);
        }
    }

    /**
     * 构建带有KeyName注解的函数，自动分析该函数是get还是set
     *
     * @param method 需要构建的函数
     */
    private void buildKeyNameMethod(Method method, String keyName, String[] set) {
        if (keyName == null || (keyName = keyName.trim()).length() == 0)
            return;
        int parameterCount = method.getParameterCount();
        if (parameterCount >= 2)
            return;
        String rawName = method.getName();
        if (parameterCount != 0) {
            if (rawName.startsWith("get"))
                return;
            method.setAccessible(true);
            TypeTranslate typeTranslate = Translate.createTranslate(method.getGenericParameterTypes()[0]);
            MethodSet methodSet = new MethodSet(method, typeTranslate);
            capableSet.put(keyName, methodSet);
            appendValueSet(set, methodSet);
        } else if (method.getReturnType() != void.class) {
            if (rawName.startsWith("set"))
                return;
            method.setAccessible(true);
            MethodGet methodGet = new MethodGet(method);
            capableGet.put(keyName, methodGet);
            existGet.put(keyName, methodGet);
        }
    }

    /**
     * 构建普通字段值
     *
     * @param field 字段对象
     * @param name  字段名称
     * @param set   字段设置名称
     */
    private void buildCommonField(Field field, String name, String[] set) {
        String rawName;
        if (name == null || (name = name.trim()).length() == 0) {
            rawName = field.getName();
            name = translateName(rawName);
        } else
            rawName = name;
        field.setAccessible(true);
        if (!field.isAnnotationPresent(IgnoreSet.class)) {
            TypeTranslate typeTranslate = Translate.createTranslate(field.getGenericType());
            FieldSet fieldSet = new FieldSet(field, typeTranslate);
            capableSet.putIfAbsent(name, fieldSet);
            appendValueSet(set, fieldSet);
        }
        if (!field.isAnnotationPresent(IgnoreGet.class)) {
            FieldGet fieldGet = new FieldGet(field);
            capableGet.putIfAbsent(name, fieldGet);
            existGet.putIfAbsent(rawName, fieldGet);
        }
    }

    /**
     * 获取构建时的对象类型，也就是这个对象class,可以通过这个class来实例化一个对象
     *
     * @return 对象类型
     */
    public Class<T> getType() {
        return type;
    }

    /**
     * 转换Json或类对象的名称
     *
     * @param name 需要转换的名称
     * @return 转换后的名称
     */
    private static String translateName(String name) {
        StringBuilder translate = new StringBuilder();
        for (char c : name.toCharArray()) {
            if (c == '_')
                continue;
            if (c == '-')
                continue;
            if (c >= 'A' && c <= 'Z')
                translate.append((char) (c + 32));
            else
                translate.append(c);
        }
        return translate.toString();
    }

    /**
     * 实例化类
     *
     * @return 类对象，如果实例化失败，则返回null
     */
    public T newInstance() {
        try {
            return type.getDeclaredConstructor().newInstance();
        } catch (Exception ignored) {
            return null;
        }
    }

    /**
     * 将Map对象的值填充到已有的实例化对象中
     */
    public void fillInstance(Map<?, ?> map, T object) {
        if (map == null)
            throw new NullPointerException("map对象不能为空");
        if (object == null)
            throw new NullPointerException("目标对象不能为空");
        map.forEach((name, value) -> {
            if (name == null)
                return;
            fill(object, name.toString(), value);
        });
    }

    /**
     * 将Map对象的值转换成对象
     *
     * @return 类对象，如果实例化失败，则返回null
     */
    public T newInstance(Map<?, ?> map) {
        if (map == null)
            throw new NullPointerException("map对象不能为空");
        T object = newInstance();
        map.forEach((name, value) -> {
            if (name == null)
                return;
            set(object, name.toString(), value);
        });
        return object;
    }

    /**
     * 获取能够设置值的字段，这个字段不一定是具体的某个成员变量，也可以是函数，比如void setID(value)
     *
     * @param name 需要被设置的值的名称，如果想要获取函数的字段，不需要加set，比如setID只需要传入id即可
     * @return 值设置对象，可以通过这个对象来为object设置值，设置的值将会自动进行转换
     */
    public ValueSet getCapableSet(String name) {
        ValueSet valueSet = capableSet.get(name);
        if (valueSet == null)
            valueSet = capableSet.get(translateName(name));
        if (valueSet == null)
            for (RegexValueSet regin : regexSet)
                if (Pattern.matches(regin.regex, name))
                    return regin.valueSet;
        return valueSet;
    }

    /**
     * 获取能够被获取值的字段，这个字段不一定是具体的某个成员变量，也可以是函数，比如Object getID(void)
     *
     * @param name 需要被获取的值的名称，如果想要获取函数的字段，不需要加get，比如getID只需要传入id即可
     * @return 值获取对象，可以通过该对象来获取object中某个字段值或方法返回值
     */
    public ValueGet getCapableGet(String name) {
        ValueGet valueGet = capableGet.get(name);
        if (valueGet == null)
            valueGet = capableGet.get(translateName(name));
        return valueGet;
    }

    /**
     * 设置值，名称和值会自动匹配
     *
     * @param object 需要被设置值的对象
     * @param name   需要被设置的值的名称
     * @param value  需要设置的值
     * @return 是否设置成功
     */
    public boolean set(Object object, String name, Object value) {
        ValueSet set = getCapableSet(name);
        if (set == null)
            return false;
        try {
            set.set(object, value);
            return true;
        } catch (Exception ignored) {
        }
        return false;
    }

    /**
     * 填充值，名称和值会自动匹配，该值会和原来的值合并
     *
     * @param object 需要被填充值的对象
     * @param name   需要被填充的值的名称
     * @param value  需要填充的值
     * @return 是否填充成功
     */
    public boolean fill(Object object, String name, Object value) {
        ValueSet set = getCapableSet(name);
        if (set == null)
            return false;
        try {
            set.fill(object, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

    /**
     * 获取值
     *
     * @param object 需要被获取值的对象
     * @param name   需要被获取的值的名称
     * @return 获取的值，如果值不存在或是获取异常，则返回null
     */
    public Object get(Object object, String name) {
        ValueGet get = getCapableGet(name);
        if (get == null)
            return null;
        try {
            return get.get(object);
        } catch (Exception ignored) {
        }
        return null;
    }

    /**
     * 遍历所有可获取的键值对
     *
     * @param object 需要遍历的对象
     * @param action 键值对接收器
     */
    public void forEach(Object object, BiConsumer<? super String, Object> action) {
        existGet.forEach((key, value) -> {
            try {
                action.accept(key, value.get(object));
            } catch (Exception ignored) {
            }
        });
    }

    /**
     * 将对象转换成HashMap
     *
     * @param object 需要转换的对象
     * @return 转换后的HashMap对象
     */
    public HashMap<String, Object> getHashMap(Object object) {
        HashMap<String, Object> map = new HashMap<>();
        forEach(object, map::put);
        return map;
    }
}
